#pragma once
#include "compiler.hpp"
#include "runner.hpp"
#include <jsoncpp/json/json.h>
#include <signal.h>
#include <unistd.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"
namespace ns_compile_run
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_compiler;
    using namespace ns_runner;
    class CompileRun
    {
    private:
        static void RemoveFile(const std::string &file_name)
        {
            //实际清除文件的个数是不确定的，但是最多有哪些文件是清除的
            //判断文件存在后，再使用unlink删除相关硬链接
            std::string src = PathUtil::Src(file_name);
            if(FileUtil::IsFileExists(src)) unlink(src.c_str());
        
            std::string compile_error = PathUtil::CompilerErr(file_name);
            if(FileUtil::IsFileExists(compile_error)) unlink(compile_error.c_str());

            std::string exe = PathUtil::Exe(file_name);
            if(FileUtil::IsFileExists(exe)) unlink(exe.c_str());

            std::string run_error = PathUtil::RunErr(file_name);
            if(FileUtil::IsFileExists(run_error)) unlink(run_error.c_str());

            std::string _stdin = PathUtil::Stdin(file_name);
            if(FileUtil::IsFileExists(_stdin)) unlink(_stdin.c_str());

            std::string _stdout = PathUtil::Stdout(file_name);
            if(FileUtil::IsFileExists(_stdout)) unlink(_stdout.c_str());

            std::string _stderr = PathUtil::Stderr(file_name);
            if(FileUtil::IsFileExists(_stderr)) unlink(_stderr.c_str());
        }
        static std::string SignoToDesc(int signo)
        {
            std::string desc;
            //1~31号传统信号
            switch(signo)
            {
                case SIGABRT:
                    desc = "内存资源超出限制";
                    break;
                case SIGFPE:
                    desc = "算术异常";
                    break;
                case SIGSEGV:
                    desc = "程序发生段错误";
                    break;
                case SIGXCPU:
                    desc = "CPU运行时间超出";
                    break;
                case SIGXFSZ:
                    desc = "写入文件过大";
                    break;
                case SIGSYS:
                    desc = "用户非法系统调用";
                    break;
                default:
                    desc = "其它运行时错误";
                    break;
            }
            return desc;
        }
        /*************************
         * status:
         * > 0,运行时出错，可执行程序被信号异常终止
         * ==0，正常运行完毕
         * < 0:-1表示用户代码为空；-2表示未知错误(本质是内部逻辑错误，但不想暴露给外部)；-3表示编译错误,要求返回具体错误信息
         **************************/
        static std::string StatusToDesc(int status,const std::string &file_name)
        {
            std::string desc;
            switch(status)
            {
                case 0:
                    desc = "编译运行成功";
                    break;
                case -1:
                    desc = "用户代码为空";
                    break;
                case -2:
                    desc = "未知错误";
                    break;
                case -3:
                    desc = FileUtil::ReadFile(PathUtil::CompilerErr(file_name));
                    break;
                default:
                    desc = SignoToDesc(status);
                    break;
            }
            return desc;
        }
    public:
        /*******************************
         * 输入：in_json
         * code:用户提交代码
         * input:用户给自己提交代码对应的输入，不做处理(OJ中的自主测试)
         * cpu_limit:CPU运行时间限制
         * mem_limit:内存空间限制
         * 输出：out_json
         * 必填：
         * status:状态码
         * desc:状态码描述，请求结果如何
         * 选填：
         * stdout:代码运行完的正确结果
         * stderr:代码运行完的错误结果
         * 
         * 参数：
         * in_json:code,input,cpu_limit,mem_limit
         * out_json:status,desc,stdout,stderr(后面两个代码运行完才有)
        ********************************/
        static void Start(const std::string &in_json,std::string &out_json)
        {
            //对in_json反序列化
            Json::Value in_value;//存储in_json反序列化结果
            Json::Reader reader;//用以进行解析反序列化
            reader.parse(in_json,in_value);
            
            std::string code = in_value["code"].asString();
            std::string input = in_value["input"].asString();
            int cpu_limit = in_value["cpu_limit"].asInt();
            int mem_limit = in_value["mem_limit"].asInt();
            std::string mode = in_value["mode"].asString();
            std::string IO_Input;
            std::string IO_Output;
            if(mode == "IO")
            {
                IO_Input = in_value["IO_Input"].asString();
                IO_Output = in_value["IO_Output"].asString();
            }
            
            Json::Value out_value;
            int status;//状态码，用于out_value中
            std::string file_name;//形成唯一文件的文件名
            int ret;//Run返回值
            if(code.size() == 0)
            {
                status = -1;//用户代码是空的
                goto END;
            }

            //in_json反序列化,拿到用户提交信息：代码和输入--->用已形成唯一文件，以便编译和运行
            
            file_name = FileUtil::UniqueFileName();//形成唯一文件名,没有目录，没有后缀，纯文件名

            if(!FileUtil::WriteFile(PathUtil::Src(file_name),code))//将获取的用户提交代码内容写入指定的唯一文件中
            {
                //其实是写入文件错误，但不希望暴露给用户
                status = -2;
                goto END;                
            }
            //已经获取到源文件，编译和运行
            if(!Compiler::Compile(file_name))
            {
                status = -3;
                goto END;
            }
            
            ret = Runner::Run(file_name,IO_Input,IO_Output,mode,cpu_limit,mem_limit);
            if(ret < 0)
            {
                status = -2;
            }
            else if(ret >= 0)
            {
                status = ret;
            }
            END:
            //统一进行处理
            out_value["status"] = status;
            out_value["desc"] = StatusToDesc(status,file_name);
            out_value["res"] = 0;//默认没有AC
            if(status == 0)
            {
                //运行成功,存在运行成功或错误结果
                std::string output = FileUtil::ReadFile(PathUtil::Stdout(file_name),true);
                if(mode == "IO")
                {
                    int flag = 0;
                    output = JudgeUtil::Judge(output,IO_Output,flag);
                    out_value["res"] = flag;
                }
                out_value["stdout"] = output;
                out_value["stderr"] = FileUtil::ReadFile(PathUtil::Stderr(file_name));
                //程序成功运行完,错误输出为空,说明一定是答案正确
                if(mode != "IO" && out_value["stderr"].asString().empty())
                    out_value["res"] = 1;
            }
            
            //进行序列化
            Json::StyledWriter writer;
            out_json = writer.write(out_value);

            //此时已经拿到所有结果，可以将所有临时文件清除
            RemoveFile(file_name);
        }
    };
}
